package asCachePool
{
	import asCachePool.interfaces.ICacheInfo;

	/**
	 * LRU内存缓存(即如果大于缓存最大size时,使用次数最次的先出)
	 * 
	 * 本组件多用于管理加载外部swf缓存在内存
	 * 为了判断数据是否最新,加了一个version即版本号做为判断
	 * 例如角色服装服务器更新了,可能客户端读取缓存就有可能读到旧的服装数据
	 * 
	 * 
	 * var rc:MemoryCache = new MemoryCache();
	 * 
	 * var obj:TestObj = new TestObj();
	 * // 加载数据要转二进制才能存放到到硬盘缓存
	 * rc.putInCache("test1",obj);
	 * var res:TestObj = rc.getCache("test1") as TestObj;
	 * 
	 * @author Pelephone
	 * @website http://cnblogs.com/pelephone
	 */
	public class MemoryCache extends BaseCache
	{
		/**
		 * 内存缓存图形 <String,PoolInfo>
		 */
		private var cacheMap:Object = {};
		/**
		 * 双链表来写LRU算法,即用次数最少的缓存先删除.<String>,存url即cacheMap的主键
		 * 算法思路,每get和new数据到cacheMap时都把数据放入下表的表头.
		 * 这样表最前面的肯定是用最多的数据,最后的是用最少的.而且还可以用此表知道已缓存的数据长度
		 */		
		private var lruLinks:Vector.<String>;
		
//		private var _clearPeriod:int = 1000*60;
//		private var _expired:int = 1000*60;
		
		/**
		 * 给缓存分组,组名映射对应主键名数组<String,<Vector.<String>>>
		 
		private var groupMap:Object;*/
		
		public function MemoryCache(size:int=500)
		{
			cacheMap = {};
			lruLinks = new Vector.<String>();
//			groupMap = {};
			super(size);
		}
		
		/**
		 *  为了判断数据是否最新,加了一个version即版本号做为判断
		 * 例如角色服装服务器更新了,可能客户端读取缓存就有可能读到旧的服装数据
		 * @param keyName
		 * @param body
		 * @param version
		 * @param groupName
		 
		public function putInCache(keyName:String,body:Object,version:int=0,groupName:String=null):void
		{
			var lCache:ICacheInfo = newPoolInf(keyName,body);
			cacheMap[keyName] = lCache;
			
			//有新缓存对象就加入队首,get的时候也把对象放入队首.清除的时候从后面清就可以LRU移除
			//如果>=0表示keyName已缓存有对象,则cacheMap重盖了对象,lruLinks表就不需要添元素,反之lruLinks往前加元素
			if(lruLinks.indexOf(keyName)<0)
				lruLinks.unshift(keyName);
			
			if(groupName){
				lCache.setGroupName(groupName);
				var arr:Vector.<String> = groupMap[groupName];
				if(!arr) groupMap[groupName] = new Vector.<String>();
				groupMap[groupName].push(keyName);
			}
			
			lruRemoveCache();
		}*/
		
		/**
		 * 新建池缓存信息对象
		 * @param keyName
		 * @param body
		 */
		protected function newPoolInf(keyName:String,body:Object):ICacheInfo
		{
			var lCache:ICacheInfo = new CacheInfo();
			lCache.setKeyName(keyName);
			lCache.setBody(body);
			lCache.setUpdateTime((new Date()).getTime()/1000);
			lCache.setCount(0);
//			lCache.setExpired(expired);
//			lCache.version = version;
			return lCache;
		}
		
		override public function clearExpired():void
		{
			if(lruLinks.length==0) return;
			var poolInf:ICacheInfo = lruLinks[lruLinks.length-1] as ICacheInfo;
			// 过期的时间点
			var pTime:int = poolInf.getUpdateTime() + poolInf.getExpired();
			// 当前时间
			var getTime:int = (new Date()).getTime()/1000;
			// 过期时间点比当前时间大表示该缓存已过期
			// 例如过期时间是8点，而当前时间是9点，则对象过期，要清除
			while(pTime<getTime && lruLinks.length>0)
			{
				removeCache(poolInf.getKeyName());
				
				if(lruLinks.length==0) break;
				
				poolInf = lruLinks[lruLinks.length-1] as ICacheInfo;
				pTime = poolInf.getUpdateTime() + poolInf.getExpired();
			}
		}
		
		override public function getCache(keyName:String):*
		{
			var cacheInf:ICacheInfo = getCacheInfo(keyName);
			if(!cacheInf) return null;
			return super.getCache(keyName);
		}
		
		override public function getCacheInfo(keyName:String):ICacheInfo
		{
			var tid:int = lruLinks.indexOf(keyName);
			if(tid<0) {
//				trace("无",keyName,"对应的缓存对象!");
				return null;
			}
			//LRU算法,每获取一次就把对象放入对首,这样用得最少的自然就在队尾了.
			lruLinks.splice(tid,1);
			lruLinks.unshift(keyName);
			
			return cacheMap[keyName] as ICacheInfo;
		}
		
		override public function hasCache(keyName:String):Boolean
		{
			return (lruLinks.indexOf(keyName)>=0);
		}
		
		override public function putCacheInfo(keyName:String, cacheInf:ICacheInfo):ICacheInfo
		{
			cacheMap[keyName] = cacheInf;
			
			//有新缓存对象就加入队首,get的时候也把对象放入队首.清除的时候从后面清就可以LRU移除
			//如果>=0表示keyName已缓存有对象,则cacheMap重盖了对象,lruLinks表就不需要添元素,反之lruLinks往前加元素
			if(lruLinks.indexOf(keyName)<0)
				lruLinks.unshift(keyName);
			
//			if(groupName){
//				lCache.setGroupName(groupName);
//				var arr:Vector.<String> = groupMap[groupName];
//				if(!arr) groupMap[groupName] = new Vector.<String>();
//				groupMap[groupName].push(keyName);
//			}
			
			lruRemoveCache();
			return cacheInf;
		}
		
		override public function putInCache(keyName:String, body:Object):*
		{
			var lCache:ICacheInfo = newPoolInf(keyName,body);
			putCacheInfo(keyName,lCache);
			return body;
		}
		
		override public function removeCache(keyName:String):void
		{
			if(!hasCache(keyName)) {
				trace("缓存对象不存在!");
				return;
			}
			var tid:int = lruLinks.indexOf(keyName);
			lruLinks.splice(tid,1);
			cacheMap[keyName] = null;
			delete cacheMap[keyName];
		}
		
		override public function removeAllCache():void
		{
			cacheMap = {};
			lruLinks = new Vector.<String>();
		}
		
		/**
		 * 超出缓存的数量就开始移出
		 */
		private function lruRemoveCache():void
		{
			while(capacity>0 && lruLinks.length>capacity)
			{
				var disStr:String = lruLinks.pop();
				removeCache(disStr);
			}
		}
		
		//////////////////////////////////////////
		// getter/setter
		//////////////////////////////////////////

		override public function set capacity(value:int):void
		{
			super.capacity = value;
			lruRemoveCache();
		}
		
		/**
		 * 获取一组缓存(此方法会遍历数组里面的所有对象，应尽量少用）
		 * @param groupName
		 * @return 
		 
		 public function getGroup(groupName:String):Array
		 {
		 var resArr:Array = [];
		 var keys:Vector.<String> = groupMap[groupName];
		 for each (var keyName:String in keys) 
		 {
		 var lcache:ICacheInfo = cacheMap[keyName] as ICacheInfo;
		 resArr.push(lcache.getBody());
		 }
		 return resArr;
		 }*/
		
		/**
		 * 移出一组缓存
		 * @param groupName
		 
		 public function removeGroup(groupName:String):void
		 {
		 var ls:Vector.<String> = groupMap[groupName] as Vector.<String>;
		 if(!ls) return;
		 for each (var keyName:String in ls)
		 {
		 removeCache(keyName);
		 }
		 delete groupMap[groupName];
		 }*/
		
		/**
		 * 整理组里面没有对应对象的键,将其移出
		 * @param groupName
		 * @return 
		 
		 public function flushGroup(groupName:String):void
		 {
		 var arr:Vector.<String> = groupMap[groupName];
		 if(!arr) return;
		 for (var i:int = 0; i < arr.length; i++)
		 {
		 var keyName:String = arr[i];
		 if(!getCache(keyName))
		 arr.splice(i--,1);
		 }
		 if(!arr.length) 
		 delete groupMap[groupName];
		 }*/
		
		/**
		 * 从组里面删除
		 * @param keyName
		 
		 private function removeFromGroup(keyName:String):void
		 {
		 var lcache:ICacheInfo = cacheMap[keyName] as ICacheInfo;
		 if(!lcache || !lcache.getGroupName()) return;
		 var arr:Vector.<String> = groupMap[lcache.getGroupName()] as Vector.<String>;
		 var gid:int = arr.indexOf(keyName);
		 arr.splice(gid,1);
		 }*/
	}
}